﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Collections.ObjectModel;
using Indigo = System.IdentityModel.Tokens;

namespace Microsoft.Cryptography.WCF
{
    /// <summary>
    /// Custom X509SecurityToken is required to support RSA-Signatures with SHA-2 encryption in XML-Signatures
    /// </summary>
    public class X509SecurityToken : Indigo.X509SecurityToken
    {
        string _syncRoot = string.Empty;
        ReadOnlyCollection<Indigo.SecurityKey> _securityKeys;

        /// <summary>
        /// Custom X509SecurityToken is required to support RSA-Signatures with SHA-2 encryption in XML-Signatures
        /// </summary>
        /// <param name="certificate">Certificate, which should be used as security token</param>
        public X509SecurityToken(X509Certificate2 certificate)
            : base(certificate)
        {
        }

        /// <summary>
        /// Custom X509SecurityToken is required to support RSA-Signatures with SHA-2 encryption in XML-Signatures
        /// </summary>
        /// <param name="certificate">Certificate, which should be used as security token. Sets the <see cref="Microsoft.Cryptography.WCF.X509SecurityToken"/>.Certificate property.</param>
        /// <param name="id">A unique identifier of the security token. Sets the Microsoft.Cryptography.WCF.X509SecurityToken.Id property.</param>
        public X509SecurityToken(X509Certificate2 certificate, string id)
            : base(certificate, id)
        {
        }

        /// <summary>
        /// Returns a value indicating whether the key identifier for this instance is equal to the specified  key identifier.
        /// </summary>
        /// <param name="keyIdentifierClause">An <see cref="System.IdentityModel.Tokens.SecurityKeyIdentifierClause"/> to compare to this instance.</param>
        /// <returns>Returns a value indicating whether the key identifier for this instance is equal to the specified  key identifier.</returns>
        public override bool MatchesKeyIdentifierClause(System.IdentityModel.Tokens.SecurityKeyIdentifierClause keyIdentifierClause)
        {
            bool matches = base.MatchesKeyIdentifierClause(keyIdentifierClause);

            if (!matches && (keyIdentifierClause is System.IdentityModel.Tokens.LocalIdKeyIdentifierClause))
            {
                return ((System.IdentityModel.Tokens.LocalIdKeyIdentifierClause)keyIdentifierClause).LocalId == this.Id &&
                    ((((System.IdentityModel.Tokens.LocalIdKeyIdentifierClause)keyIdentifierClause).OwnerType == typeof(Microsoft.Cryptography.WCF.X509SecurityToken)) ||
                    (((System.IdentityModel.Tokens.LocalIdKeyIdentifierClause)keyIdentifierClause).OwnerType == typeof(System.IdentityModel.Tokens.X509SecurityToken)));

            }

            return matches;
        }

        /// <summary>
        /// Gets the cryptographic keys associated with the security token
        /// </summary>
        public override ReadOnlyCollection<Indigo.SecurityKey> SecurityKeys
        {
            get
            {
                if (_securityKeys == null)
                {
                    lock (_syncRoot)
                    {
                        if (_securityKeys == null)
                        {
                            List<Indigo.SecurityKey> temp = new List<Indigo.SecurityKey>(1);
                            temp.Add(new X509AsymmetricSecurityKey(this.Certificate));
                            _securityKeys = temp.AsReadOnly();
                        }
                    }
                    
                }

                return _securityKeys;
            }
        }

    }
}

